/** @file
  Debug Agent timer lib for OMAP 35xx.

  Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/
#include <Base.h>
#include <Library/BaseLib.h>
#include <Library/IoLib.h>
#include <Library/OmapLib.h>
#include <Library/ArmLib.h>
#include <Library/PcdLib.h>

#include <Omap3530/Omap3530.h>


volatile UINT32 gVector;

// Cached registers
volatile UINT32 gTISR;
volatile UINT32 gTCLR;
volatile UINT32 gTLDR;
volatile UINT32 gTCRR;
volatile UINT32 gTIER;

VOID
EnableInterruptSource (
  VOID
  )
{
  UINTN Bank;
  UINTN Bit;

  // Map vector to FIQ, IRQ is default
  MmioWrite32 (INTCPS_ILR (gVector), 1);

  Bank = gVector / 32;
  Bit  = 1UL << (gVector % 32);

  MmioWrite32 (INTCPS_MIR_CLEAR(Bank), Bit);
}

VOID
DisableInterruptSource (
  VOID
  )
{
  UINTN Bank;
  UINTN Bit;

  Bank = gVector / 32;
  Bit  = 1UL << (gVector % 32);

  MmioWrite32 (INTCPS_MIR_SET(Bank), Bit);
}



/**
  Setup all the hardware needed for the debug agents timer.

  This function is used to set up debug enviroment. It may enable interrupts.

**/
VOID
EFIAPI
DebugAgentTimerIntialize (
  VOID
  )
{
  UINT32      TimerBaseAddress;
  UINT32      TimerNumber;

  TimerNumber = PcdGet32(PcdOmap35xxDebugAgentTimer);
  gVector = InterruptVectorForTimer (TimerNumber);

  // Set up the timer registers
  TimerBaseAddress = TimerBase (TimerNumber);
  gTISR = TimerBaseAddress + GPTIMER_TISR;
  gTCLR = TimerBaseAddress + GPTIMER_TCLR;
  gTLDR = TimerBaseAddress + GPTIMER_TLDR;
  gTCRR = TimerBaseAddress + GPTIMER_TCRR;
  gTIER = TimerBaseAddress + GPTIMER_TIER;

  if ((TimerNumber < 2) || (TimerNumber > 9)) {
    // This code assumes one the General Purpose timers is used
    // GPT2 - GPT9
    CpuDeadLoop ();
  }
  // Set source clock for GPT2 - GPT9 to SYS_CLK
  MmioOr32 (CM_CLKSEL_PER, 1 << (TimerNumber - 2));

}


/**
  Set the period for the debug agent timer. Zero means disable the timer.

  @param[in] TimerPeriodMilliseconds    Frequency of the debug agent timer.

**/
VOID
EFIAPI
DebugAgentTimerSetPeriod (
  IN  UINT32  TimerPeriodMilliseconds
  )
{
  UINT64      TimerCount;
  INT32       LoadValue;

  if (TimerPeriodMilliseconds == 0) {
    // Turn off GPTIMER3
    MmioWrite32 (gTCLR, TCLR_ST_OFF);

    DisableInterruptSource ();
  } else {
    // Calculate required timer count
    TimerCount = DivU64x32(TimerPeriodMilliseconds * 1000000, PcdGet32(PcdDebugAgentTimerFreqNanoSeconds));

    // Set GPTIMER5 Load register
    LoadValue = (INT32) -TimerCount;
    MmioWrite32 (gTLDR, LoadValue);
    MmioWrite32 (gTCRR, LoadValue);

    // Enable Overflow interrupt
    MmioWrite32 (gTIER, TIER_TCAR_IT_DISABLE | TIER_OVF_IT_ENABLE | TIER_MAT_IT_DISABLE);

    // Turn on GPTIMER3, it will reload at overflow
    MmioWrite32 (gTCLR, TCLR_AR_AUTORELOAD | TCLR_ST_ON);

    EnableInterruptSource ();
  }
}


/**
  Perform End Of Interrupt for the debug agent timer. This is called in the
  interrupt handler after the interrupt has been processed.

**/
VOID
EFIAPI
DebugAgentTimerEndOfInterrupt (
  VOID
  )
{
   // Clear all timer interrupts
  MmioWrite32 (gTISR, TISR_CLEAR_ALL);

  // Poll interrupt status bits to ensure clearing
  while ((MmioRead32 (gTISR) & TISR_ALL_INTERRUPT_MASK) != TISR_NO_INTERRUPTS_PENDING);

  MmioWrite32 (INTCPS_CONTROL, INTCPS_CONTROL_NEWFIQAGR);
  ArmDataSynchronizationBarrier ();

}

